package org.jupiter.blog.authentic.config;

import org.apache.commons.lang3.StringUtils;
import org.jupiter.blog.api.domain.PermissionUrlResourceInfo;
import org.jupiter.blog.authentic.constant.AuthenticConstant;
import org.jupiter.blog.authentic.utils.AuthenticUtils;
import org.jupiter.blog.common.constant.SymbolConstant;
import org.jupiter.blog.redis.component.SpringRedisRepository;
import org.jupiter.blog.service.mapper.JupiterPermissionUrlMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 权限初始化器 系统启动时将权限数据初始化到Redis中
 *
 * @author lwl
 * @date 2025/1/27
 */
@Component
public class PermissionInitializer implements CommandLineRunner {

	private static final Logger logger = LoggerFactory.getLogger(PermissionInitializer.class);

	@Autowired
	private JupiterPermissionUrlMapper permissionUrlMapper;

	@Autowired
	private SpringRedisRepository redisRepository;

	@Override
	public void run(String... args) throws Exception {
		logger.info("Starting to initialize permission data to Redis...");

		try{
			// 1. Get all permission-URL resource associations from database
			List<PermissionUrlResourceInfo> permissionUrlResources = permissionUrlMapper.selectPermissionUrlResourceInfo();
			logger.info("Retrieved {} permission-URL resource associations from database", permissionUrlResources.size());

			if (permissionUrlResources.isEmpty()) {
				logger.warn("No permission-URL resource associations found, skipping initialization");
				return;
			}

			// 2. Group by HTTP method
			Map<String, List<PermissionUrlResourceInfo>> permissionsByMethod = permissionUrlResources.stream()
					.collect(Collectors.groupingBy(PermissionUrlResourceInfo::getMethod));

			// 3. Create URL sets and permission mappings for each HTTP method
			for (Map.Entry<String, List<PermissionUrlResourceInfo>> entry : permissionsByMethod.entrySet()){
				String method = entry.getKey();
				List<PermissionUrlResourceInfo> methodPermissions = entry.getValue();

				// Extract URL set
				Set<String> urls = methodPermissions.stream()
						.map(PermissionUrlResourceInfo::getUrl)
						.filter(StringUtils::isNotBlank)
						.collect(Collectors.toSet());

				// Build Redis key: ALL_URL_SET_PREFIX + method
				String urlSetKey = AuthenticUtils.getRedisKey(AuthenticConstant.ALL_URL_SET_PREFIX, method);

				// Clear old URL set
				redisRepository.remove(urlSetKey);

				// Store URL set to Redis
				if (!urls.isEmpty()){
					String[] urlArray = urls.toArray(new String[0]);
					redisRepository.setAdd(urlSetKey, urlArray);
					logger.info("Initialized {} URLs for HTTP method {} to Redis key: {}",
							urls.size(), method, urlSetKey);
				}

				// Group by URL to collect all permissions for each URL
				Map<String, List<PermissionUrlResourceInfo>> permissionsByUrl = methodPermissions.stream()
						.collect(Collectors.groupingBy(PermissionUrlResourceInfo::getUrl));

				// Create permission sets for each URL
				for (Map.Entry<String, List<PermissionUrlResourceInfo>> urlEntry : permissionsByUrl.entrySet()) {
					String url = urlEntry.getKey();
					List<PermissionUrlResourceInfo> urlPermissions = urlEntry.getValue();

					if (StringUtils.isNotBlank(url)) {
						// Build Redis key: URL_PERMISSION_PREFIX + method + ":" + url
						String urlPermissionKey = AuthenticUtils.getRedisKey(AuthenticConstant.URL_PERMISSION_PREFIX, 
								method, SymbolConstant.COLON, url);
						
						// Clear old permission set for this URL
						redisRepository.remove(urlPermissionKey);
						
						// Add all permission codes for this URL
						for (PermissionUrlResourceInfo info : urlPermissions) {
							if (StringUtils.isNotBlank(info.getPermissionCode())) {
								redisRepository.setAdd(urlPermissionKey, info.getPermissionCode());
							}
						}
						
						logger.debug("Initialized {} permissions for URL {} {} to Redis key: {}",
								urlPermissions.size(), method, url, urlPermissionKey);
					}
				}
			}

			logger.info("Permission data initialization completed! Processed {} methods with {} total associations",
					permissionsByMethod.size(), permissionUrlResources.size());

		} catch (Exception e){
			logger.error("Permission data initialization failed", e);
			throw e;
		}
	}

}
